#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
行业股票技术面分析工具 - 技术分析模块

本模块使用pandas和numpy原生函数实现了各种技术指标的计算，
包括MACD、RSI、KDJ、布林带等。

作者: Claude
日期: 2024-05-14
"""

import numpy as np
import pandas as pd
import logging
import config
from utils import retry

class TechnicalAnalyzer:
    """技术分析器类"""
    
    def __init__(self):
        """初始化技术分析器"""
        self.params = config.INDICATOR_PARAMS
        self.weights = config.WEIGHT_CONFIG
        self.logger = logging.getLogger('technical_analysis')
    
    def calculate_all_indicators(self, df):
        """计算所有技术指标
        
        Args:
            df: 数据框，包含 OHLCV 数据
            
        Returns:
            df: 添加了技术指标的数据框
        """
        # 确保数据框包含必要的列
        required_cols = ['日期', '收盘', '开盘', '最高', '最低', '成交额']
        missing_cols = [col for col in required_cols if col not in df.columns]
        
        if missing_cols:
            self.logger.error(f"数据框缺少必要的列: {missing_cols}")
            return df
        
        # 确保按日期排序
        df = df.sort_values('日期')
        
        # 计算各项技术指标
        df = self.calculate_macd(df)
        df = self.calculate_rsi(df)
        df = self.calculate_kdj(df)
        df = self.calculate_bollinger_bands(df)
        df = self.calculate_moving_averages(df)
        df = self.calculate_volume_indicators(df)
        
        return df
    
    def calculate_macd(self, df):
        """计算MACD指标
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了MACD指标的数据框
        """
        close = df['收盘'].values
        
        # 计算EMA
        ema_fast = self._calculate_ema(close, self.params['macd_fast'])
        ema_slow = self._calculate_ema(close, self.params['macd_slow'])
        
        # 计算MACD线
        macd_line = ema_fast - ema_slow
        
        # 计算信号线
        signal_line = self._calculate_ema(macd_line, self.params['macd_signal'])
        
        # 计算MACD柱状图
        macd_histogram = macd_line - signal_line
        
        # 添加到数据框
        df['MACD'] = macd_line
        df['MACD_Signal'] = signal_line
        df['MACD_Hist'] = macd_histogram
        
        return df
    
    def calculate_rsi(self, df):
        """计算RSI指标
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了RSI指标的数据框
        """
        close = df['收盘'].values
        period = self.params['rsi_period']
        
        # 计算价格变化
        delta = np.zeros_like(close)
        delta[1:] = close[1:] - close[:-1]
        
        # 分别计算上涨和下跌
        gain = np.where(delta > 0, delta, 0)
        loss = np.where(delta < 0, -delta, 0)
        
        # 计算平均上涨和下跌
        avg_gain = np.zeros_like(close)
        avg_loss = np.zeros_like(close)
        
        # 计算第一个值
        if len(gain) >= period:
            avg_gain[period-1] = np.mean(gain[:period])
            avg_loss[period-1] = np.mean(loss[:period])
            
            # 计算后续值
            for i in range(period, len(close)):
                avg_gain[i] = (avg_gain[i-1] * (period-1) + gain[i]) / period
                avg_loss[i] = (avg_loss[i-1] * (period-1) + loss[i]) / period
        
        # 计算相对强度
        rs = np.zeros_like(close)
        rs[avg_loss > 0] = avg_gain[avg_loss > 0] / avg_loss[avg_loss > 0]
        
        # 计算RSI
        rsi = 100 - (100 / (1 + rs))
        
        # 添加到数据框
        df['RSI'] = rsi
        
        return df
    
    def calculate_kdj(self, df):
        """计算KDJ指标
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了KDJ指标的数据框
        """
        high = df['最高'].values
        low = df['最低'].values
        close = df['收盘'].values
        
        n = self.params['kdj_k']
        m1 = self.params['kdj_d']
        m2 = self.params['kdj_j']
        
        # 计算最高价和最低价的N日移动最大值和最小值
        high_n = pd.Series(high).rolling(window=n).max().values
        low_n = pd.Series(low).rolling(window=n).min().values
        
        # 计算RSV
        rsv = np.zeros_like(close)
        denom = high_n - low_n
        # 避免除以零
        valid_idx = denom != 0
        rsv[valid_idx] = 100 * (close[valid_idx] - low_n[valid_idx]) / denom[valid_idx]
        
        # 计算K值、D值、J值
        k = np.zeros_like(close)
        d = np.zeros_like(close)
        j = np.zeros_like(close)
        
        # 计算第一个有效值
        for i in range(n, len(close)):
            if i == n:
                k[i] = 50
                d[i] = 50
            else:
                k[i] = (m1 - 1) * k[i-1] / m1 + rsv[i] / m1
                d[i] = (m2 - 1) * d[i-1] / m2 + k[i] / m2
            
            j[i] = 3 * k[i] - 2 * d[i]
        
        # 添加到数据框
        df['KDJ_K'] = k
        df['KDJ_D'] = d
        df['KDJ_J'] = j
        
        return df
    
    def calculate_bollinger_bands(self, df):
        """计算布林带指标
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了布林带指标的数据框
        """
        close = df['收盘'].values
        period = self.params['boll_period']
        std_mult = self.params['boll_std']
        
        # 计算移动平均线
        sma = pd.Series(close).rolling(window=period).mean().values
        
        # 计算标准差
        std = pd.Series(close).rolling(window=period).std().values
        
        # 计算布林带上下轨
        upper = sma + std_mult * std
        lower = sma - std_mult * std
        
        # 计算带宽
        bandwidth = (upper - lower) / sma * 100
        
        # 计算%B值
        percent_b = np.zeros_like(close)
        denom = upper - lower
        # 避免除以零
        valid_idx = denom != 0
        percent_b[valid_idx] = (close[valid_idx] - lower[valid_idx]) / denom[valid_idx]
        
        # 添加到数据框
        df['BOLL_SMA'] = sma
        df['BOLL_Upper'] = upper
        df['BOLL_Lower'] = lower
        df['BOLL_Bandwidth'] = bandwidth
        df['BOLL_PercentB'] = percent_b
        
        return df
    
    def calculate_moving_averages(self, df):
        """计算移动平均线
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了移动平均线的数据框
        """
        close = df['收盘'].values
        
        # 计算各周期的移动平均线
        for period in self.params['ma_periods']:
            ma = pd.Series(close).rolling(window=period).mean().values
            df[f'MA{period}'] = ma
        
        return df
    
    def calculate_volume_indicators(self, df):
        """计算成交量指标
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了成交量指标的数据框
        """
        volume = df['成交额'].values
        close = df['收盘'].values
        
        # 计算成交量移动平均
        volume_ma5 = pd.Series(volume).rolling(window=5).mean().values
        volume_ma10 = pd.Series(volume).rolling(window=10).mean().values
        
        # 计算成交量变化率
        volume_change = np.zeros_like(volume)
        volume_change[1:] = (volume[1:] / volume[:-1] - 1) * 100
        
        # 计算量价关系指标 (OBV - On-Balance Volume)
        obv = np.zeros_like(volume)
        obv[0] = volume[0]
        
        for i in range(1, len(volume)):
            if close[i] > close[i-1]:
                obv[i] = obv[i-1] + volume[i]
            elif close[i] < close[i-1]:
                obv[i] = obv[i-1] - volume[i]
            else:
                obv[i] = obv[i-1]
        
        # 添加到数据框
        df['Volume_MA5'] = volume_ma5
        df['Volume_MA10'] = volume_ma10
        df['Volume_Change'] = volume_change
        df['OBV'] = obv
        
        return df
    
    def _calculate_ema(self, data, period):
        """计算指数移动平均
        
        Args:
            data: 数据数组
            period: 周期
            
        Returns:
            ema: 指数移动平均数组
        """
        ema = np.zeros_like(data)
        alpha = 2 / (period + 1)
        
        # 计算第一个值 (使用SMA)
        if len(data) >= period:
            ema[period-1] = np.mean(data[:period])
            
            # 计算后续值
            for i in range(period, len(data)):
                ema[i] = alpha * data[i] + (1 - alpha) * ema[i-1]
        
        return ema
    
    def calculate_macd_score(self, df):
        """计算MACD指标得分
        
        Args:
            df: 数据框
            
        Returns:
            score: MACD得分 (0-100)
        """
        try:
            if df.empty or 'MACD' not in df.columns or 'MACD_Signal' not in df.columns:
                self.logger.warning("计算MACD得分时缺少必要列")
                return 50  # 返回中性评分
            
            # 获取最近几天的MACD数据
            if len(df) < 5:
                self.logger.warning("MACD得分计算的数据点不足")
                return 50  # 返回中性评分
                
            recent = df.iloc[-5:].copy()
            
            # 检查是否有缺失值
            if recent['MACD'].isna().any() or recent['MACD_Signal'].isna().any():
                self.logger.warning("MACD数据存在缺失值")
                return 50  # 返回中性评分
                
            score = 50  # 基础得分
            
            # 检查金叉和死叉信号
            if recent['MACD'].iloc[-1] > recent['MACD_Signal'].iloc[-1]:
                # MACD线在信号线之上
                if recent['MACD'].iloc[-2] <= recent['MACD_Signal'].iloc[-2]:
                    # 金叉 - 强烈看涨信号
                    score += 30
                else:
                    # MACD维持在信号线以上
                    score += 20
                
                # 检查MACD值是否为正
                if recent['MACD'].iloc[-1] > 0:
                    score += 10
                
                # 检查MACD方向
                if recent['MACD'].iloc[-1] > recent['MACD'].iloc[-2]:
                    score += 10
                
            else:
                # MACD线在信号线之下
                if recent['MACD'].iloc[-2] >= recent['MACD_Signal'].iloc[-2]:
                    # 死叉 - 强烈看跌信号
                    score -= 30
                else:
                    # MACD维持在信号线以下
                    score -= 20
                
                # 检查MACD值是否为负
                if recent['MACD'].iloc[-1] < 0:
                    score -= 10
                
                # 检查MACD方向
                if recent['MACD'].iloc[-1] < recent['MACD'].iloc[-2]:
                    score -= 10
            
            # 限制分数范围在0-100之间
            return max(0, min(100, score))
            
        except Exception as e:
            self.logger.error(f"计算MACD得分时出错: {e}")
            return 50  # 出错时返回中性评分
    
    def calculate_rsi_score(self, df):
        """计算RSI指标得分
        
        Args:
            df: 数据框
            
        Returns:
            score: RSI得分 (0-100)
        """
        try:
            if df.empty or 'RSI' not in df.columns:
                self.logger.warning("计算RSI得分时缺少必要列")
                return 50  # 返回中性评分
            
            # 获取最近的RSI值
            if len(df) < 1:
                self.logger.warning("RSI得分计算的数据点不足")
                return 50  # 返回中性评分
                
            recent_rsi = df['RSI'].iloc[-1]
            
            # 买入区域、中性区域、卖出区域的分界点
            rsi_buy = self.params['rsi_buy']
            rsi_sell = self.params['rsi_sell']
            
            if pd.isna(recent_rsi):
                self.logger.warning("RSI值为空")
                return 50  # 无法计算时给予中性评分
            
            # 基于RSI值给予得分
            if recent_rsi <= 20:
                # 强烈超卖
                score = 90
            elif recent_rsi <= rsi_buy:
                # 超卖
                score = 80
            elif recent_rsi <= 50:
                # 偏向超卖
                score = 60
            elif recent_rsi < rsi_sell:
                # 偏向超买
                score = 40
            elif recent_rsi < 80:
                # 超买
                score = 20
            else:
                # 强烈超买
                score = 10
            
            # 考虑RSI变化趋势
            if len(df) >= 3:
                try:
                    rsi_trend = df['RSI'].iloc[-1] - df['RSI'].iloc[-3]
                    if rsi_trend > 0 and df['RSI'].iloc[-1] < rsi_sell:
                        # RSI上升且未达超买 - 看涨
                        score += 10
                    elif rsi_trend < 0 and df['RSI'].iloc[-1] > rsi_buy:
                        # RSI下降且未达超卖 - 看跌
                        score -= 10
                except Exception as e:
                    self.logger.warning(f"计算RSI趋势时出错: {e}")
            
            # 限制分数范围
            return max(0, min(100, score))
            
        except Exception as e:
            self.logger.error(f"计算RSI得分时出错: {e}")
            return 50  # 出错时返回中性评分
    
    def calculate_kdj_score(self, df):
        """计算KDJ指标得分
        
        Args:
            df: 数据框
            
        Returns:
            score: KDJ得分 (0-100)
        """
        try:
            if df.empty or 'KDJ_K' not in df.columns or 'KDJ_D' not in df.columns:
                self.logger.warning("计算KDJ得分时缺少必要列")
                return 50  # 返回中性评分
            
            # 获取最近几天的KDJ数据
            if len(df) < 3:
                self.logger.warning("KDJ得分计算的数据点不足")
                return 50  # 返回中性评分
                
            recent = df.iloc[-3:].copy()
            
            if recent['KDJ_K'].isna().any() or recent['KDJ_D'].isna().any():
                self.logger.warning("KDJ数据存在缺失值")
                return 50  # 无法计算时给予中性评分
            
            score = 50  # 基础得分
            
            # 检查KDJ金叉和死叉信号
            if recent['KDJ_K'].iloc[-1] > recent['KDJ_D'].iloc[-1]:
                # K线在D线之上
                if recent['KDJ_K'].iloc[-2] <= recent['KDJ_D'].iloc[-2]:
                    # 金叉 - 强烈看涨信号
                    score += 25
                else:
                    # K线维持在D线以上
                    score += 15
            else:
                # K线在D线之下
                if recent['KDJ_K'].iloc[-2] >= recent['KDJ_D'].iloc[-2]:
                    # 死叉 - 强烈看跌信号
                    score -= 25
                else:
                    # K线维持在D线以下
                    score -= 15
            
            # 考虑KDJ数值区间
            k_value = recent['KDJ_K'].iloc[-1]
            if k_value < 20:
                # 超卖区域
                score += 20
            elif k_value > 80:
                # 超买区域
                score -= 20
            
            # J值超买超卖加强信号
            if 'KDJ_J' in df.columns and not recent['KDJ_J'].isna().any():
                j_value = recent['KDJ_J'].iloc[-1]
                if j_value < 0:
                    # 超卖信号更强
                    score += 10
                elif j_value > 100:
                    # 超买信号更强
                    score -= 10
            
            # 限制分数范围
            return max(0, min(100, score))
            
        except Exception as e:
            self.logger.error(f"计算KDJ得分时出错: {e}")
            return 50  # 出错时返回中性评分
    
    def calculate_bollinger_score(self, df):
        """计算布林带指标得分
        
        Args:
            df: 数据框
            
        Returns:
            score: 布林带得分 (0-100)
        """
        try:
            if df.empty or 'BOLL_PercentB' not in df.columns:
                self.logger.warning("计算布林带得分时缺少必要列")
                return 50  # 返回中性评分
            
            # 获取最近几天的布林带数据
            if len(df) < 5:
                self.logger.warning("布林带得分计算的数据点不足")
                return 50  # 返回中性评分
                
            recent = df.iloc[-5:].copy()
            
            if recent['BOLL_PercentB'].isna().any():
                self.logger.warning("布林带数据存在缺失值")
                return 50  # 无法计算时给予中性评分
            
            score = 50  # 基础得分
            
            # 根据价格在布林带中的位置评分
            percent_b = recent['BOLL_PercentB'].iloc[-1]
            
            if percent_b < 0:
                # 价格低于下轨 - 极度超卖
                score += 30
            elif percent_b < 0.2:
                # 价格接近下轨 - 超卖
                score += 20
            elif percent_b < 0.5:
                # 价格在下半轨 - 偏向看涨
                score += 10
            elif percent_b < 0.8:
                # 价格在上半轨 - 偏向看跌
                score -= 10
            elif percent_b < 1:
                # 价格接近上轨 - 超买
                score -= 20
            else:
                # 价格高于上轨 - 极度超买
                score -= 30
            
            # 检查布林带宽度趋势
            if len(recent) >= 5 and 'BOLL_Bandwidth' in df.columns and not recent['BOLL_Bandwidth'].isna().any():
                try:
                    bandwidth_change = recent['BOLL_Bandwidth'].iloc[-1] - recent['BOLL_Bandwidth'].iloc[-5]
                    
                    if bandwidth_change > 0:
                        # 带宽扩大 - 波动性增加
                        if percent_b > 0.8 or percent_b < 0.2:
                            # 价格在轨道边缘，带宽扩大意味着趋势可能继续
                            score += 10 if percent_b < 0.2 else -10
                    else:
                        # 带宽收窄 - 波动性减小，可能准备突破
                        score += 5
                except Exception as e:
                    self.logger.warning(f"计算布林带宽度趋势时出错: {e}")
            
            # 检查价格是否在突破布林带
            if '收盘' in df.columns and len(df) >= 2:
                try:
                    if percent_b > 1 and df['收盘'].iloc[-1] > df['收盘'].iloc[-2]:
                        # 向上突破且继续上涨
                        score -= 5
                    elif percent_b < 0 and df['收盘'].iloc[-1] < df['收盘'].iloc[-2]:
                        # 向下突破且继续下跌
                        score += 5
                except Exception as e:
                    self.logger.warning(f"检查价格突破布林带时出错: {e}")
            
            # 限制分数范围
            return max(0, min(100, score))
            
        except Exception as e:
            self.logger.error(f"计算布林带得分时出错: {e}")
            return 50  # 出错时返回中性评分
    
    def calculate_ma_score(self, df):
        """计算均线系统得分
        
        Args:
            df: 数据框
            
        Returns:
            score: 均线得分 (0-100)
        """
        try:
            if df.empty:
                self.logger.warning("计算均线得分时数据为空")
                return 50  # 返回中性评分
            
            # 检查是否有必要的MA列
            ma_columns = [f'MA{period}' for period in self.params['ma_periods']]
            missing_columns = [col for col in ma_columns if col not in df.columns]
            
            if missing_columns:
                self.logger.warning(f"计算均线得分时缺少列: {missing_columns}")
                return 50  # 返回中性评分
            
            # 获取最近的数据
            if len(df) < 3:
                self.logger.warning("均线得分计算的数据点不足")
                return 50  # 返回中性评分
                
            recent = df.iloc[-1].copy()
            
            # 检查是否有缺失值
            for col in ma_columns:
                if pd.isna(recent[col]):
                    self.logger.warning(f"均线数据存在缺失值: {col}")
                    return 50  # 无法计算时给予中性评分
            
            # 获取收盘价
            if '收盘' not in df.columns or pd.isna(recent['收盘']):
                self.logger.warning("均线得分计算缺少收盘价数据")
                return 50  # 返回中性评分
                
            close = recent['收盘']
            
            # 获取各均线价格
            ma_values = [recent[f'MA{period}'] for period in self.params['ma_periods']]
            
            # 判断各均线的位置关系
            score = 50  # 基础得分
            
            # 检查价格相对于均线的位置
            above_all = True
            below_all = True
            
            for ma in ma_values:
                if close < ma:
                    above_all = False
                if close > ma:
                    below_all = False
            
            if above_all:
                # 价格在所有均线之上 - 强势
                score += 20
            elif below_all:
                # 价格在所有均线之下 - 弱势
                score -= 20
            
            # 检查均线是否多头排列（短期均线在长期均线上方）
            is_bullish = True
            is_bearish = True
            
            for i in range(len(ma_values) - 1):
                if ma_values[i] < ma_values[i+1]:
                    is_bearish = False
                if ma_values[i] > ma_values[i+1]:
                    is_bullish = False
            
            if is_bullish:
                # 均线多头排列 - 强势
                score += 25
            elif is_bearish:
                # 均线空头排列 - 弱势
                score -= 25
            
            # 考虑短期均线和长期均线的交叉情况
            if len(df) >= 3 and 'MA5' in df.columns and 'MA20' in df.columns:
                try:
                    prev = df.iloc[-3]
                    ma5_prev = prev['MA5']
                    ma20_prev = prev['MA20']
                    
                    ma5_curr = recent['MA5']
                    ma20_curr = recent['MA20']
                    
                    if not pd.isna(ma5_prev) and not pd.isna(ma20_prev):
                        if ma5_curr > ma20_curr and ma5_prev <= ma20_prev:
                            # 5日均线上穿20日均线 - 金叉，看涨信号
                            score += 15
                        elif ma5_curr < ma20_curr and ma5_prev >= ma20_prev:
                            # 5日均线下穿20日均线 - 死叉，看跌信号
                            score -= 15
                except Exception as e:
                    self.logger.warning(f"计算均线交叉时出错: {e}")
            
            # 限制分数范围
            return max(0, min(100, score))
            
        except Exception as e:
            self.logger.error(f"计算均线得分时出错: {e}")
            return 50  # 出错时返回中性评分
    
    def calculate_volume_score(self, df):
        """计算成交量指标得分
        
        Args:
            df: 数据框
            
        Returns:
            score: 成交量得分 (0-100)
        """
        try:
            if df.empty or '成交额' not in df.columns or 'Volume_MA5' not in df.columns:
                self.logger.warning("计算成交量得分时缺少必要列")
                return 50  # 返回中性评分
            
            # 获取近期数据
            if len(df) < 5:
                self.logger.warning("成交量得分计算的数据点不足")
                return 50  # 返回中性评分
                
            recent = df.iloc[-5:].copy()
            
            # 检查是否有缺失值
            if recent['成交额'].isna().any() or recent['Volume_MA5'].isna().any():
                self.logger.warning("成交量数据存在缺失值")
                return 50  # 无法计算时给予中性评分
                
            score = 50  # 基础得分
            
            # 获取最近一天的数据
            latest = recent.iloc[-1]
            
            # 成交量相对于5日均量
            if latest['Volume_MA5'] > 0:
                vol_ratio = latest['成交额'] / latest['Volume_MA5']
            else:
                vol_ratio = 1
            
            # 价格变化
            price_change = 0
            if '收盘' in df.columns and len(recent) > 1:
                try:
                    price_change = (latest['收盘'] / recent.iloc[-2]['收盘'] - 1) * 100
                except Exception as e:
                    self.logger.warning(f"计算价格变化时出错: {e}")
            
            # 量价配合评分
            if price_change > 0:
                # 价格上涨
                if vol_ratio > 1.5:
                    # 放量上涨 - 强势信号
                    score += 25
                elif vol_ratio > 1:
                    # 小幅放量上涨 - 积极信号
                    score += 15
                else:
                    # 缩量上涨 - 上涨乏力
                    score += 5
            else:
                # 价格下跌
                if vol_ratio > 1.5:
                    # 放量下跌 - 弱势信号
                    score -= 25
                elif vol_ratio > 1:
                    # 小幅放量下跌 - 消极信号
                    score -= 15
                else:
                    # 缩量下跌 - 观望信号
                    score -= 5
            
            # 连续成交量变化趋势
            try:
                vol_trend = sum(1 if recent['成交额'].iloc[i] > recent['成交额'].iloc[i-1] else -1 for i in range(1, len(recent)))
                
                if vol_trend > 0:
                    # 成交量持续增加
                    if price_change > 0:
                        # 价格上涨 + 成交量增加 = 强势
                        score += 15
                    else:
                        # 价格下跌 + 成交量增加 = 弱势
                        score -= 15
                elif vol_trend < 0:
                    # 成交量持续减少
                    if price_change < 0:
                        # 价格下跌 + 成交量减少 = 下跌动能减弱
                        score += 5
                    else:
                        # 价格上涨 + 成交量减少 = 上涨乏力
                        score -= 5
            except Exception as e:
                self.logger.warning(f"计算成交量趋势时出错: {e}")
            
            # OBV指标变化
            if 'OBV' in df.columns and not recent['OBV'].isna().any():
                try:
                    obv_change = latest['OBV'] - recent.iloc[0]['OBV']
                    
                    if obv_change > 0:
                        # OBV上升 - 积极信号
                        score += 10
                    else:
                        # OBV下降 - 消极信号
                        score -= 10
                except Exception as e:
                    self.logger.warning(f"计算OBV变化时出错: {e}")
            
            # 限制分数范围
            return max(0, min(100, score))
            
        except Exception as e:
            self.logger.error(f"计算成交量得分时出错: {e}")
            return 50  # 出错时返回中性评分
    
    def calculate_score(self, df):
        """计算综合技术面评分
        
        Args:
            df: 数据框
            
        Returns:
            df: 添加了综合得分的数据框
        """
        if df is None or df.empty:
            self.logger.warning("计算综合得分时数据为空")
            # 返回空数据框
            return df if df is not None else pd.DataFrame()
        
        try:
            # 计算各项指标得分
            macd_score = self.calculate_macd_score(df)
            rsi_score = self.calculate_rsi_score(df)
            kdj_score = self.calculate_kdj_score(df)
            boll_score = self.calculate_bollinger_score(df)
            ma_score = self.calculate_ma_score(df)
            volume_score = self.calculate_volume_score(df)
            
            # 记录各个得分
            self.logger.info(f"MACD得分: {macd_score}, RSI得分: {rsi_score}, KDJ得分: {kdj_score}")
            self.logger.info(f"布林带得分: {boll_score}, 均线得分: {ma_score}, 成交量得分: {volume_score}")
            
            # 检查是否有None或NaN值，如果有则替换为中性评分50
            scores = {'macd': macd_score, 'rsi': rsi_score, 'kdj': kdj_score, 
                     'boll': boll_score, 'ma': ma_score, 'volume': volume_score}
            
            for key, score in scores.items():
                if score is None or pd.isna(score):
                    self.logger.warning(f"{key}得分为空，使用中性评分50")
                    scores[key] = 50
            
            # 按权重计算综合得分
            total_score = (
                scores['macd'] * self.weights['macd'] +
                scores['rsi'] * self.weights['rsi'] +
                scores['kdj'] * self.weights['kdj'] +
                scores['boll'] * self.weights['boll'] +
                scores['ma'] * self.weights['ma'] +
                scores['volume'] * self.weights['volume']
            )
            
            # 四舍五入到整数
            rounded_score = round(total_score)
            
            # 将各项得分添加到数据框
            df['MACD得分'] = scores['macd']
            df['RSI得分'] = scores['rsi']
            df['KDJ得分'] = scores['kdj']
            df['布林带得分'] = scores['boll']
            df['均线得分'] = scores['ma']
            df['成交量得分'] = scores['volume']
            df['综合得分'] = rounded_score
            
            self.logger.info(f"计算出的综合得分: {rounded_score}")
            
            return df
            
        except Exception as e:
            self.logger.error(f"计算综合得分时出错: {e}")
            # 出错时添加默认得分
            for score_name in ['MACD得分', 'RSI得分', 'KDJ得分', '布林带得分', '均线得分', '成交量得分']:
                df[score_name] = 50
            df['综合得分'] = 50
            return df 